package com.atguigu.gmall.order.biz.impl;

import java.util.Date;

import java.util.*;

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.atguigu.gmall.cart.entity.CartItem;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.Jsons;
import com.atguigu.gmall.common.util.UserAuthUtils;
import com.atguigu.gmall.enums.OrderStatus;
import com.atguigu.gmall.enums.ProcessStatus;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.product.SkuDetailFeignClient;
import com.atguigu.gmall.feign.user.UserFeignClient;
import com.atguigu.gmall.feign.ware.WareFeignClient;
import com.atguigu.gmall.order.entity.OrderDetail;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.order.entity.OrderStatusLog;
import com.atguigu.gmall.order.entity.PaymentInfo;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.service.OrderStatusLogService;
import com.atguigu.gmall.order.service.PaymentInfoService;
import com.atguigu.gmall.order.to.OrderMsgTo;
import com.atguigu.gmall.order.vo.DetailVo;
import com.atguigu.gmall.order.vo.OrderSubmitVo;
import com.atguigu.gmall.seckill.vo.SeckillOrderSubmitVo;
import com.atguigu.gmall.user.entity.UserAddress;
import com.atguigu.gmall.util.consts.MqConst;
import com.atguigu.gmall.ware.WareSkuMapVo;
import com.atguigu.gmall.ware.WareStockMsg;
import com.atguigu.gmall.ware.WareStockResultMsg;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;

import com.atguigu.gmall.order.biz.OrderBizService;
import com.atguigu.gmall.order.vo.OrderConfirmVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

/**
 * @author lfy
 * @Description 订单业务逻辑组件
 * @create 2022-10-19 11:45
 */
@Slf4j
@Service
public class OrderBizServiceImpl implements OrderBizService {

    @Autowired
    OrderInfoMapper orderInfoMapper;
    @Autowired
    OrderInfoService orderInfoService;

    @Autowired
    OrderDetailService orderDetailService;

    @Autowired
    CartFeignClient cartFeignClient;

    @Autowired
    UserFeignClient userFeignClient;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;

    @Autowired
    OrderStatusLogService orderStatusLogService;

    @Autowired
    PaymentInfoService paymentInfoService;

    @Autowired
    WareFeignClient wareFeignClient;

    @Override
    public void changeOrderStatusByCAS(List<OrderStatus> expectOrderStatus,
                                       List<ProcessStatus> expectProcessStatus,
                                       OrderStatus orderStatus,
                                       ProcessStatus processStatus,
                                       Long orderId,
                                       Long userId) {

        List<String> os = expectOrderStatus.stream().map(item -> item.name()).collect(Collectors.toList());

        List<String> ps = expectProcessStatus.stream().map(item -> item.name()).collect(Collectors.toList());

        orderInfoMapper.updateOrderStatusByCAS(os,
                ps,
                orderStatus.name(),
                processStatus.name(),
                orderId,
                userId);
    }

    @Override
    public OrderConfirmVo getOrderConfirmData() {
        OrderConfirmVo confirmVo = new OrderConfirmVo();

        //1、订单中的商品集合【获取购物车中选中的商品列表】
        List<CartItem> cartItems = cartFeignClient.getCheckedCartItems().getData();
        List<DetailVo> detailVos = cartItems.stream()
                .map(cartItem -> {
                    DetailVo detailVo = new DetailVo();
                    detailVo.setImgUrl(cartItem.getSkuDefaultImg());
                    detailVo.setSkuName(cartItem.getSkuName());
                    detailVo.setOrderPrice(cartItem.getSkuPrice());
                    detailVo.setSkuNum(cartItem.getSkuNum());
                    detailVo.setSkuId(cartItem.getSkuId());
                    //查询每个商品的库存是否有 http://localhost:9001/hasStock?skuId=42&num=9992
                    String hasStock = wareFeignClient.hasStock(cartItem.getSkuId(), cartItem.getSkuNum());
                    detailVo.setHasStock(hasStock);
                    return detailVo;
                }).collect(Collectors.toList());
        confirmVo.setDetailArrayList(detailVos);


        //2、总数量：
        Integer totalNum = detailVos.stream()
                .map(DetailVo::getSkuNum)
                .reduce((o1, o2) -> o1 + o2)
                .get();
        confirmVo.setTotalNum(totalNum);

        //3、总价格
        BigDecimal totalAmount = detailVos.stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum().toString())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        confirmVo.setTotalAmount(totalAmount);

        //4、用户收货地址列表
        Result<List<UserAddress>> userAddress = userFeignClient.getUserAddress();
        confirmVo.setUserAddressList(userAddress.getData());


        //5、交易号；唯一标识；充当一个订单id
        String token = UUID.randomUUID().toString().replace("-", "");
        //给页面一个令牌
        confirmVo.setTradeNo(token);
        //给redis存储一个，用来防重
        redisTemplate.opsForValue().set(RedisConst.ORDER_TOKEN + token, "x", 30, TimeUnit.MINUTES);

        return confirmVo;
    }

    @Scheduled(cron = "0 */30 * ? * *")
    private void scanAndCloseOrder() {
        //扫描拿到数据库所有已过期未关闭的订单
        //整个关闭订单；
    }

    @Override
    public void closeOrder(Long orderId, Long userId) {
        //1、当前订单如果是未支付则关闭？只要支付了数据库就会改为已支付
        //update order_info_1 set order_status='CLOSED',process_status='CLOSED' where id=111

        //同一时刻并发修改同一个数据为不同值。为了安全起见，加锁。
        //加锁比较重量级。 悲观锁。
        //轻量级：乐观锁

        //分布式锁。保证订单的业务状态是正确
        //   1）、支付：改为已支付 =====  关单：发现已经支付就不用关。未支付状态的订单改为已关闭
        //   2）、关单：订单未支付改为已关闭 ==== 支付：无条件改为已支付状态

        //上来就加锁：  悲观锁；浪费性能
        //当前业务，只会在某个临界点偶尔发生并发风险。  乐观锁
        // version=1  version=2  类似CAS思想的改订单状态；
        // 改为已关闭，前提条件是未支付
        changeOrderStatusByCAS(Arrays.asList(OrderStatus.UNPAID), Arrays.asList(ProcessStatus.UNPAID),
                OrderStatus.CLOSED, ProcessStatus.CLOSED, orderId, userId);

    }


    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public Long submitOrder(OrderSubmitVo submitVo, String tradeNo) {
        //submitVo 【进行业务校验】
        //1、业务校验-防重校验【不允许重复提交，重复提交的请求只处理一次】[验令牌保证原子性，否则有可能产生并发问题]
        String script = "if redis.call(\"exists\",KEYS[1])\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        Long aLong = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Arrays.asList(RedisConst.ORDER_TOKEN + tradeNo));
        if (aLong == 0) {
            //删除失败； 假令牌 -  真令牌但是被校验过后删除了
            throw new GmallException(ResultCodeEnum.REQ_REPEAT);
        }


        //2、业务校验-验库存
        List<OrderSubmitVo.OrderDetailListDTO> collect = submitVo.getOrderDetailList()
                .stream()
                .filter(item -> {
                    Long skuId = item.getSkuId();
                    Integer skuNum = item.getSkuNum();
                    String stock = wareFeignClient.hasStock(skuId, skuNum);
                    return !"1".equals(stock);
                })
                .collect(Collectors.toList());
        if (collect != null && collect.size() > 0) {
            throw new GmallException(ResultCodeEnum.SKU_HAS_NO_STOCK);
        }


        //3、业务校验-验价【前端提交来的每个商品，价格都进行校验】
        //收集所有前端价格和后台实时价格不相等的商品
        List<OrderSubmitVo.OrderDetailListDTO> detailListDTOS = submitVo.getOrderDetailList().stream()
                .filter(item -> {
                    BigDecimal orderPrice = item.getOrderPrice();
                    //实时查询最新价格
                    Result<BigDecimal> skuPrice = skuDetailFeignClient.getSkuPrice(item.getSkuId());
                    return !skuPrice.getData().equals(orderPrice);
                }).collect(Collectors.toList());
        if (detailListDTOS != null && detailListDTOS.size() > 0) {
            //发现前后价格不符的商品，提示用户，刷新页面重新确认价格
            throw new GmallException(ResultCodeEnum.SKU_PRICE_CHANGE);
        }


        //===========以上都校验通过===============

        //3、根据前端提交来的下单数据，保存订单
        Long orderId = saveOrder(submitVo, tradeNo);

        //4、移除购物车中选中的商品
        cartFeignClient.deleteChecked();

        //5、30min以后，如果此订单未支付就关闭订单
        Long userId = UserAuthUtils.getUserAuthInfo().getUserId();


//        ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
//        service.schedule(()->{
//            closeOrder(orderId,userId);
//        },30,TimeUnit.MINUTES);
        OrderMsgTo msgTo = new OrderMsgTo(orderId, userId);
        rabbitTemplate.convertAndSend(MqConst.ORDER_EVENT_EXCHANGE, MqConst.RK_ORDER_CREATED, Jsons.toStr(msgTo));
        log.info("订单创建成功：并发送消息给MQ： {}", msgTo);
        return orderId;
    }

    @Override
    public Long saveOrder(OrderSubmitVo submitVo, String tradeNo) {
        //1、保存订单 order_info
        OrderInfo orderInfo = saveOrderInfo(submitVo, tradeNo);

        //2、保存订单明细 order_detail
        saveOrderDetail(submitVo, orderInfo);

        //3、保存此次订单操作日志 order_status_log
        saveOrderStatusLog(orderInfo);

        return orderInfo.getId();
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId, Long userId) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getId, orderId)
                .eq(OrderInfo::getUserId, userId);
        OrderInfo info = orderInfoService.getOne(wrapper);
        return info;
    }

    @Override
    public void updateOrderStatusPayed(String json) {
        //1、拿到需要修改的订单的内容【支付宝异步通知给我们的】
        Map<String, String> content = Jsons.toObj(json, new TypeReference<Map<String, String>>() {
        });

        //2、保存支付信息
        PaymentInfo paymentInfo = preparePaymentInfoData(content);
        paymentInfoService.save(paymentInfo);


        //3、修改订单状态
        changeOrderStatusByCAS(Arrays.asList(OrderStatus.CLOSED, OrderStatus.UNPAID),
                Arrays.asList(ProcessStatus.UNPAID, ProcessStatus.CLOSED),
                OrderStatus.PAID, ProcessStatus.PAID, Long.parseLong(paymentInfo.getOrderId()), paymentInfo.getUserId());

        log.info("订单已支付状态修改完成");

        //4、调用库存系统进行扣库存和出库； RPC（在线等）、消息（只需要对方发命令要求做事）
        //远程调用购物车要到选中的商品;  消息（提高系统的性能）
        //90%~99%响应时间、平均响应时间短、QPS、吞吐量高
        WareStockMsg msg = prepareWareStockMsg(Long.parseLong(paymentInfo.getOrderId()), paymentInfo.getUserId());
        rabbitTemplate.convertAndSend(MqConst.WARE_STOCK_EXCHANGE,
                MqConst.RK_WARE_STOCK,
                Jsons.toStr(msg));

    }

    @Override
    public void updateOrderStatusByStockResult(WareStockResultMsg resultMsg) {
        Long orderId = resultMsg.getOrderId();
        String status = resultMsg.getStatus();
        OrderInfo orderInfo = orderInfoService.getOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getId, orderId));
        //现在肯定是已支付；
        ProcessStatus ps = null; //订单最终修改成什么状态
        OrderStatus os = null;
        switch (status){
            case "DEDUCTED": ps = ProcessStatus.NOTIFIED_WARE; os = OrderStatus.WAITING_DELEVER; break;
            case "OUT_OF_STOCK": ps = ProcessStatus.STOCK_EXCEPTION; os = OrderStatus.WAITING_SCHEDULE; break;
        }

        //把订单的已支付状态改为 等待发货等
        changeOrderStatusByCAS(Arrays.asList(OrderStatus.PAID),
                Arrays.asList(ProcessStatus.PAID),os,ps,orderId,orderInfo.getUserId());

        //状态的流转： UNPAID -- PAID
        //订单状态回退： 订单的所有操作都是一个单方向的流
    }

    @Override
    public List<WareStockMsg> splitOrder(Long orderId, String wareSkuMapJson) {
        // [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]
        //1、得到当前订单的仓库分布
        List<WareSkuMapVo> mapVos = Jsons.toObj(wareSkuMapJson, new TypeReference<List<WareSkuMapVo>>() {
        });

        //2、得到当前订单【父订单（老订单）】
        OrderInfo orderInfo = orderInfoService.getById(orderId);
        //当时老订单买了多少商品
        List<OrderDetail> orderDetails = orderDetailService.getOrderDetails(orderId, orderInfo.getUserId());


        //每一种仓库分布就产生一个订单
        List<WareStockMsg> result = mapVos.stream().map(mapVo -> {
            //保存子订单和子订单明细并返回给库存系统 拆单数据
            WareStockMsg stockMsg = saveChildOrder(mapVo, orderInfo, orderDetails);
            return stockMsg;
        }).collect(Collectors.toList());

        //===========子订单创建成功以后进行大订单状态修改=============
        //3、大订单改为已拆分
        changeOrderStatusByCAS(Arrays.asList(OrderStatus.PAID),
                Arrays.asList(ProcessStatus.PAID),
                OrderStatus.SPLIT,ProcessStatus.SPLIT,orderInfo.getId(),orderInfo.getUserId());
        return result;
    }

    @Override
    public Long saveSeckillOrder(OrderInfo orderSubmitVo) {

        //1、保存订单
        int insert = orderInfoMapper.insert(orderSubmitVo);
        Long orderId = orderSubmitVo.getId();
        //2、保存明细
        List<OrderDetail> detailList = orderSubmitVo.getOrderDetailList();
        for (OrderDetail detail : detailList) {
            detail.setOrderId(orderId);
        }
        orderDetailService.saveBatch(detailList);

        return orderId;
    }

    /**
     * 根据 库存分布得到一个 子订单
     * @param mapVo
     * @param orderInfo
     * @param orderDetails
     * @return
     */
    private WareStockMsg saveChildOrder(WareSkuMapVo mapVo,
                                     OrderInfo orderInfo,
                                     List<OrderDetail> orderDetails) {




        OrderInfo childOrder = new OrderInfo();

        childOrder.setConsignee(orderInfo.getConsignee());
        childOrder.setConsigneeTel(orderInfo.getConsigneeTel());
        //库存系统会告诉我们这些商品在同一个仓库，为这些商品生产一个小订单
        List<Long> skuIds = mapVo.getSkuIds();

        //当前子订单需要负责的所有商品
        List<OrderDetail> details = orderDetails.stream()
                .filter(item -> skuIds.contains(item.getSkuId()))
                .collect(Collectors.toList());


        //计算子订单的总额
        BigDecimal totalAmount = details.stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum().toString())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        childOrder.setTotalAmount(totalAmount);

        childOrder.setOrderStatus(orderInfo.getOrderStatus());
        childOrder.setUserId(orderInfo.getUserId());


        childOrder.setPaymentWay(orderInfo.getPaymentWay());
        childOrder.setDeliveryAddress(orderInfo.getDeliveryAddress());
        childOrder.setOrderComment(orderInfo.getOrderComment());
        childOrder.setOutTradeNo(orderInfo.getOutTradeNo());
        childOrder.setTradeBody(details.get(0).getSkuName());

        childOrder.setCreateTime(new Date());
        childOrder.setExpireTime(orderInfo.getExpireTime());


        childOrder.setProcessStatus(orderInfo.getProcessStatus());
        childOrder.setTrackingNo("");
        //父订单id
        childOrder.setParentOrderId(orderInfo.getId());
        childOrder.setImgUrl(details.get(0).getImgUrl());
        childOrder.setProvinceId(0L);
        childOrder.setOperateTime(new Date());
        childOrder.setActivityReduceAmount(new BigDecimal("0"));
        childOrder.setCouponAmount(new BigDecimal("0"));
        childOrder.setOriginalTotalAmount(totalAmount);
        childOrder.setFeightFee(new BigDecimal("0"));

        //保存子订单到数据库
        orderInfoService.save(childOrder);

        //保存子订单负责的所有商品
        List<OrderDetail> childDetails = details.stream().map(item -> {
            OrderDetail orderDetail = new OrderDetail();
            BeanUtils.copyProperties(item, orderDetail);
            //设置不一样的项：订单id一定写子订单id
            orderDetail.setOrderId(childOrder.getId());
            orderDetail.setId(null);
            return orderDetail;
        }).collect(Collectors.toList());
        orderDetailService.saveBatch(childDetails);



        //准备返回
        WareStockMsg stockMsg = new WareStockMsg();
        stockMsg.setOrderId(childOrder.getId());
        stockMsg.setConsignee(childOrder.getConsignee());
        stockMsg.setConsigneeTel(childOrder.getConsigneeTel());
        stockMsg.setOrderComment(childOrder.getOrderComment());
        stockMsg.setOrderBody(childOrder.getTradeBody());
        stockMsg.setDeliveryAddress(childOrder.getDeliveryAddress());
        stockMsg.setPaymentWay("2"); //alipay

        List<WareStockMsg.Sku> collect = childDetails.stream().map(item -> {
            WareStockMsg.Sku sku = new WareStockMsg.Sku();
            sku.setSkuId(item.getSkuId());
            sku.setSkuNum(item.getSkuNum());
            sku.setSkuName(item.getSkuName());
            return sku;
        }).collect(Collectors.toList());
        stockMsg.setDetails(collect);
        //仓库id
        stockMsg.setWareId(mapVo.getWareId());
        //返回给库存系统的内容
        return stockMsg;
    }

    private WareStockMsg prepareWareStockMsg(Long orderId, Long userId) {
        OrderInfo orderInfo = getOrderInfo(orderId, userId);
        WareStockMsg stockMsg = new WareStockMsg();

        stockMsg.setOrderId(orderId);
        stockMsg.setConsignee(orderInfo.getConsignee());
        stockMsg.setConsigneeTel(orderInfo.getConsigneeTel());
        stockMsg.setOrderComment(orderInfo.getOrderComment());
        stockMsg.setOrderBody(orderInfo.getTradeBody());
        stockMsg.setDeliveryAddress(orderInfo.getDeliveryAddress());
        stockMsg.setPaymentWay("2");


        //订单的详情数据拿到
        List<OrderDetail> details = orderDetailService.getOrderDetails(orderId, userId);

        List<WareStockMsg.Sku> skus = details.stream().map(item -> {
            WareStockMsg.Sku sku = new WareStockMsg.Sku();
            sku.setSkuId(item.getSkuId());
            sku.setSkuNum(item.getSkuNum());
            sku.setSkuName(item.getSkuName());
            return sku;
        }).collect(Collectors.toList());

        stockMsg.setDetails(skus);

        return stockMsg;
    }

    private PaymentInfo preparePaymentInfoData(Map<String, String> content) {

        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setOutTradeNo(content.get("out_trade_no"));
//        UserAuthUtils.getUserAuthInfo(); //feign远程调用--web请求
        //从第一次发消息开始用户id就一直往下传
        //再次根据out_trade_no 查询一下订单
        OrderInfo orderInfo = orderInfoService.getOne(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getOutTradeNo, paymentInfo.getOutTradeNo()));


        paymentInfo.setUserId(orderInfo.getUserId());
        paymentInfo.setOrderId(orderInfo.getId().toString());
        paymentInfo.setPaymentType("ALIPAY");
        paymentInfo.setTradeNo(content.get("trade_no"));
        paymentInfo.setTotalAmount(new BigDecimal(content.get("total_amount")));
        paymentInfo.setSubject(content.get("subject"));
        paymentInfo.setPaymentStatus(content.get("trade_status"));
        paymentInfo.setCreateTime(new Date());
        paymentInfo.setCallbackTime(new Date());
        //
        paymentInfo.setCallbackContent(Jsons.toStr(content));

        return paymentInfo;
    }


    private void saveOrderStatusLog(OrderInfo orderInfo) {
        OrderStatusLog statusLog = new OrderStatusLog();

        statusLog.setOrderId(orderInfo.getId());
        statusLog.setUserId(orderInfo.getUserId());
        statusLog.setOrderStatus(orderInfo.getOrderStatus());
        statusLog.setOperateTime(new Date());

        orderStatusLogService.save(statusLog);
    }

    //保存订单明细
    private void saveOrderDetail(OrderSubmitVo submitVo, OrderInfo orderInfo) {
        //1、前端提交的所有要购买的商品
        List<OrderSubmitVo.OrderDetailListDTO> detailList = submitVo.getOrderDetailList();


        //2、订单明细准备：OrderDetail
        List<OrderDetail> orderDetails = detailList.stream()
                .map(item -> {
                    OrderDetail detail = new OrderDetail();
                    detail.setOrderId(orderInfo.getId()); //订单id
                    detail.setSkuId(item.getSkuId());
                    detail.setUserId(orderInfo.getUserId()); //用户id
                    detail.setSkuName(item.getSkuName());
                    detail.setImgUrl(item.getImgUrl());
                    detail.setOrderPrice(item.getOrderPrice()); //商品价格
                    detail.setSkuNum(item.getSkuNum());
                    detail.setCreateTime(new Date());

                    //记录这个商品参与的优惠
                    detail.setSplitActivityAmount(new BigDecimal("0"));
                    detail.setSplitCouponAmount(new BigDecimal("0"));

                    //这个商品的总价：  原始价格 - 参与的优惠总额
                    detail.setSplitTotalAmount(detail.getOrderPrice().multiply(new BigDecimal(detail.getSkuNum().toString())));

                    return detail;
                }).collect(Collectors.toList());

        //3、保存订单明细
        orderDetailService.saveBatch(orderDetails);

    }


    //给数据库保存订单
    private OrderInfo saveOrderInfo(OrderSubmitVo submitVo, String tradeNo) {
        OrderInfo orderInfo = new OrderInfo();

        //收货人信息
        orderInfo.setConsignee(submitVo.getConsignee());
        orderInfo.setConsigneeTel(submitVo.getConsigneeTel());
        //配送地址
        orderInfo.setDeliveryAddress(submitVo.getDeliveryAddress());


        BigDecimal totalAmount = submitVo.getOrderDetailList()
                .stream()
                .map(item -> {
                    Integer skuNum = item.getSkuNum();
                    BigDecimal orderPrice = item.getOrderPrice(); //已经验过了
                    return orderPrice.multiply(new BigDecimal(skuNum.toString()));
                }).reduce((o1, o2) -> o1.add(o2)).get();
        //订单总价： 原始金额 - 优惠额度
        orderInfo.setTotalAmount(totalAmount);

        //订单状态 未支付
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //用户id
        Long userId = UserAuthUtils.getUserAuthInfo().getUserId();
        orderInfo.setUserId(userId);

        //支付方式
        orderInfo.setPaymentWay(submitVo.getPaymentWay());

        orderInfo.setOrderComment(submitVo.getOrderComment());

        //对外交易号
        orderInfo.setOutTradeNo(tradeNo);
        //订单体【默认用订单中第一个商品的名字即可】
        String skuName = submitVo.getOrderDetailList().get(0).getSkuName();
        orderInfo.setTradeBody(skuName);

        //创建时间
        orderInfo.setCreateTime(new Date());

        //过期时间【30min 不支付代表订单过期】
        long mills = System.currentTimeMillis() + RedisConst.ORDER_TTL;
        orderInfo.setExpireTime(new Date(mills));

        //订单的处理状态【给后台看】  已支付。 一个 OrderStatus 可能代表很多中情况（处理状态）
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());

        //orderInfo.setTrackingNo("");
        //orderInfo.setParentOrderId(0L);

        String imgUrl = submitVo.getOrderDetailList().get(0).getImgUrl();
        orderInfo.setImgUrl(imgUrl);

        orderInfo.setOperateTime(new Date());

        orderInfo.setActivityReduceAmount(new BigDecimal("0"));
        orderInfo.setCouponAmount(new BigDecimal("0"));

        //原始金额
        orderInfo.setOriginalTotalAmount(totalAmount);

        //运费
        orderInfo.setFeightFee(new BigDecimal("0"));

        //可退款日期（【签收后30天】）
//        orderInfo.setRefundableTime(new Date());


        //保存订单
        orderInfoService.save(orderInfo);

        return orderInfo;
    }
}
